import { createRequire } from "module"
import net from "net"
import fs from "fs"
import fetch from "node-fetch"
import YAML from "yaml"
import plugin from "../../lib/plugins/plugin.js"

const require = createRequire(import.meta.url)
const { exec } = require("child_process")

const isPortTaken = async(port) => {
  return new Promise((resolve) => {
    const tester = net.createServer()
      .once("error", () => resolve(true))
      .once("listening", () => tester.once("close", () => resolve(false)).close())
      .listen(port)
  })
}

export class Restart extends plugin {
  constructor(e = "") {
    super({
      name: "重启",
      dsc: "#重启",
      event: "message",
      priority: 10,
      rule: [
        {
          reg: "^#重启$",
          fnc: "restart",
          permission: "master"
        }, {
          reg: "^#(停机|关机)$",
          fnc: "stop",
          permission: "master"
        }
      ]
    })

    if (e) this.e = e

    this.key = "Yz:restart"
  }

  async init() {
    let restart = await redis.get(this.key)
    if (restart) {
      restart = JSON.parse(restart)
      const uin = restart?.uin || Bot.uin
      let time = restart.time || new Date().getTime()
      time = (new Date().getTime() - time) / 1000

      let msg = `重启成功：耗时${time.toFixed(2)}秒`
      try {
        if (restart.isGroup) {
          Bot[uin].pickGroup(restart.id).sendMsg(msg)
        } else {
          Bot[uin].pickUser(restart.id).sendMsg(msg)
        }
      } catch (error) {
        /** 不发了，发不出去... */
        logger.debug(error)
      }
      redis.del(this.key)
    }
  }

  async restart() {
    let restart_port
    try {
      restart_port = YAML.parse(fs.readFileSync("./config/config/bot.yaml", "utf-8"))
      restart_port = restart_port.restart_port || 27881
    } catch { }
    await this.e.reply("开始执行重启，请稍等...")
    logger.mark(`${this.e.logFnc} 开始执行重启，请稍等...`)

    let data = JSON.stringify({
      uin: this.e?.self_id || this.e.bot.uin,
      isGroup: !!this.e.isGroup,
      id: this.e.isGroup ? this.e.group_id : this.e.user_id,
      time: new Date().getTime()
    })

    let npm = await this.checkPnpm()
    await redis.set(this.key, data, { EX: 120 })
    if (await isPortTaken(restart_port || 27881)) {
      try {
        let result = await fetch(`http://localhost:${restart_port || 27881}/restart`)
        result = await result.text()
        if (result !== "OK") {
          redis.del(this.key)
          this.e.reply("操作失败！")
          logger.error("重启失败")
        }
      } catch (error) {
        redis.del(this.key)
        this.e.reply(`操作失败！\n${error}`)
      }
    } else {
      try {
        let cm = `${npm} start`
        if (process.argv[1].includes("pm2")) {
          cm = `${npm} run restart`
        }

        exec(cm, { windowsHide: true }, (error, stdout, stderr) => {
          if (error) {
            redis.del(this.key)
            this.e.reply(`操作失败！\n${error.stack}`)
            logger.error(`重启失败\n${error.stack}`)
          } else if (stdout) {
            logger.mark("重启成功，运行已由前台转为后台")
            logger.mark(`查看日志请用命令：${npm} run log`)
            logger.mark(`停止后台运行命令：${npm} stop`)
            process.exit()
          }
        })
      } catch (error) {
        redis.del(this.key)
        let e = error.stack ?? error
        this.e.reply(`操作失败！\n${e}`)
      }
    }

    return true
  }

  async checkPnpm() {
    let npm = "npm"
    let ret = await this.execSync("pnpm -v")
    if (ret.stdout) npm = "pnpm"
    return npm
  }

  async execSync(cmd) {
    return new Promise((resolve, reject) => {
      exec(cmd, { windowsHide: true }, (error, stdout, stderr) => {
        resolve({ error, stdout, stderr })
      })
    })
  }

  async stop() {
    let restart_port
    try {
      restart_port = YAML.parse(fs.readFileSync("./config/config/bot.yaml", "utf-8"))
      restart_port = restart_port.restart_port || 27881
    } catch { }
    if (await isPortTaken(restart_port || 27881)) {
      try {
        logger.mark("关机成功，已停止运行")
        await this.e.reply("关机成功，已停止运行")
        await fetch(`http://localhost:${restart_port || 27881}/exit`)
        return
      } catch (error) {
        this.e.reply(`操作失败！\n${error}`)
        logger.error(`关机失败\n${error}`)
      }
    }

    if (!process.argv[1].includes("pm2")) {
      logger.mark("关机成功，已停止运行")
      await this.e.reply("关机成功，已停止运行")
      process.exit()
    }

    logger.mark("关机成功，已停止运行")
    await this.e.reply("关机成功，已停止运行")

    let npm = await this.checkPnpm()
    exec(`${npm} stop`, { windowsHide: true }, (error, stdout, stderr) => {
      if (error) {
        this.e.reply(`操作失败！\n${error.stack}`)
        logger.error(`关机失败\n${error.stack}`)
      }
    })
  }
}
